﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;

namespace ZackFlame.IronBrainFuck.JIT
{
    /// <summary>
    /// Представляет скомпилированную в IL BrainFuck программу.
    /// </summary>
    public sealed class BrainMethod
    {
        private DynamicMethod compiledProgram;

        internal DynamicMethod CompiledProgram
        {
            get { return compiledProgram; }
        }

        /// <summary>
        /// Возвращает исходный код BrainFuck программы.
        /// </summary>
        public string SourceCode { get; private set; }

        /// <summary>
        /// Инициализирует новый экземпляр класса-обёртки BrainMethod для
        /// скомпилированной BrainFuck программы.
        /// </summary>
        /// <param name="sourceCode">Исходный код BrainFuck программы.</param>
        /// <param name="compiledProgram">Скомпилированная программа.</param>
        internal BrainMethod(string sourceCode, DynamicMethod compiledProgram)
        {
            SourceCode = sourceCode;
            this.compiledProgram = compiledProgram;
        }        

        /// <summary>
        /// Выполняет скомпилированную программу.
        /// </summary>
        /// <param name="cells">Массив ячеек памяти BrainFuck машины.</param>
        /// <param name="cellIndex">Номер текущей ячейки BrainFuck машины.</param>
        /// <param name="input">Входной поток для считывания BrainFuck программаой.</param>
        /// <param name="output">Выходной поток для вывода BrainFuck программой.</param>
        /// <returns>Номер текущей ячейки BrainFuck машины после исполнения программы.</returns>
        public UInt16 Execute(UInt16[] cells, UInt16 cellIndex,
            TextReader input, TextWriter output)
        {
            try
            {
                return (UInt16)compiledProgram.Invoke(null,
                    new object[] { cells, cellIndex, input, output });
            }
            catch (TargetInvocationException ex)
            {
                throw ex.InnerException;
            }
        }
    }
}
